/*
 *  WorkoutPlanning.cpp
 *  Workout
 *
 *  Created by Matt Krugman on 4/24/10.
 *
 */

#include "WorkoutPrograms.h"

////////////////////////////////////////////////////////////////////////////////

WorkoutProgram::WorkoutProgram(const char * title, 
															 int startDate,
															 generateWorkoutProc gwProc,
															 int weeks /* = 12 */) : 
m_nWeeks(0),
m_startDate(20100101),
m_nW(0),
m_nCycles(3),
m_daysofweek(0),
m_basePercent(60),
m_percentProc(percentSawtooth),
m_gwProc(gwProc)
{
  
  // Define the start date
  m_startDate = startDate;
  
  // Define how long
  m_nWeeks = weeks;
  
  // Give ourselves a title for later outputing some meaning to it
  sprintf(m_title, "%s", title);
  
}

////////////////////////////////////////////////////////////////////////////////
void
WorkoutProgram::GenerateSession(void)
{
	
	int date = m_startDate;
	int base = m_basePercent;
	int dayCount = 3;
	int delta = 20;
	delta = (100 - base) / (dayCount - 1);
	int dayIncrement[3] = { 2, 2, 3};
	
	// Set all the lift goals based on the basics
  ExerciseGoalsSetAll();
	
	// Generate the specific workout
	for (int week = 0; week < m_nWeeks; week++) {
				
		// generate up the workouts for the given week
		m_gwProc(&date, week, &base, &delta, dayIncrement, dayCount, this);
		
	} // end for loop
	
	// Create the desired file outputs
  Output(OutputHTML);
  Output(OutputICAL);
	
}

////////////////////////////////////////////////////////////////////////////////

const char *
WorkoutProgram::GetTitle(OutputType t, FILE *pFile /* = NULL */)
{
  
  char title[100];
  
  // Grab the pointer to the buffer
  const char * pB;
  pB = title;
  
  for (int i = 0; i < 100; i++)
    title[i] = '\0';
  
  switch (t) {
    case OutputDebug:
      fprintf(pFile, "WorkoutProgram.m_title: %s\n", m_title);
      break;
    case OutputHTML:
      fprintf(pFile, "<h1><a href=\"#\">%s's %s Program</a></h1>\n", 
							m_title, 
							m_description);
      break;
    case OutputICAL:
      fprintf(pFile,"PRODID:-//Krugman//NONSGML %s//EN\n",m_description);
      break;
      
    default:
      break;
  }
  
  return pB;
  
}

////////////////////////////////////////////////////////////////////////////////

void
WorkoutProgram::GetWorkoutProgramDescription(OutputType t, FILE *pFile /* = NULL */)
{
  
  switch (t) {
    case OutputDebug:
      fprintf(pFile, "WorkoutProgram.m_description: %s\n", m_description);
      break;
    case OutputHTML:
      fprintf(pFile, "<h1><a href=\"#\">%s %s Program</a></h1>\n", 
							m_title, 
							m_description);
      
      fprintf(pFile, "<P>this is a test</P>\n");
      
      
      break;
    case OutputICAL:
      fprintf(pFile,"PRODID:-//Krugman//NONSGML %s//EN\n",m_title);
      break;
      
    default:
      break;
  }
  
  
}

////////////////////////////////////////////////////////////////////////////////

void
WorkoutProgram::Output(OutputType t)
{
  
  char buffer[1000];
  
  /* Input/Ouput file variables */
  FILE *pFile = NULL;
  
  char outputfile[32];
  
  // Grab the pointer to the buffer
  const char * pB;
  pB = buffer;
  
  switch (t) {
      
    case OutputICAL:
      
      printf("creating ical format\n");
      
      /* Open and create the file to write to */
      sprintf(outputfile, "%s.ics", m_title);
      pFile = fopen(outputfile,"w+");
      if (pFile == NULL)
				printf("File could not be created.\n");
      
      // Write calendar header
      fprintf(pFile,"BEGIN:VCALENDAR\n");
      fprintf(pFile,"VERSION:2.0\n");
      
      /* Document title */
      GetTitle(t,pFile);
      
      fprintf(pFile,"CALSCALE:GREGORIAN\n");
      
      /* Get all the specific workout information */
      for (int i = 0; i < m_nW; i++) {
				m_workouts[i].WorkoutShowDetails(t, pFile);
      }
      
      // Close calendar header
      fprintf(pFile,"END:VCALENDAR\n");
      
      break;
      
    case OutputHTML:
      
      /* Open and create the file to write to */
      sprintf(outputfile, "%s.html", m_title);
      pFile = fopen(outputfile,"w+");
      if (pFile == NULL)
				printf("File could not be created.\n");
      
      /* Document type */
      fprintf(pFile,"<!DOCTYPE html>\n");
      
      /* Document launguage */
      fprintf(pFile,"<html lang=\"en\">\n");
      
      /* Document head start */
      fprintf(pFile,"<head>\n");
      
      /* Document character type */
      fprintf(pFile,"<meta charset=\"utf-8\" />\n");
      //fprintf(pFile,"<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\">\n");
      
      /* Page title */
      fprintf(pFile,"<title>%i Week %s Training Program</title>\n",m_nWeeks, m_title);
      
      /* Document css */
      fprintf(pFile,"<LINK href=\"lifting.css\" rel=\"stylesheet\" type=\"text/css\">\n");
      
      /* Inject the javascript */
      //createHTMLJavascript(pFile);
      
      /* Document head close */
      fprintf(pFile,"</head>\n");
      
      /* Document body start */
      fprintf(pFile,"<body id=\"index\" class=\"home\">\n");
      
      /* Document header start */
      //fprintf(pFile,"<header=\"banner\" class=\"body\" >\n");
      
      /* Document title */
      GetTitle(t,pFile);
      
      /* Display all the exercise goals */
      ExerciseGoalsShowAll(t, pFile);
      
      /* Add Ical output link */
      fprintf(pFile,"<h2>Import Workout into Calendar Application</h2>");
      fprintf(pFile,"<P>Click on the calendar icon to import this workout into a"
							" calendar application. The format is the vCal specification that is supported in "
							" applications such as Google calendar, Mac OS X iCal.</P>");
      fprintf(pFile,"<a href=\"%s.ics\" <img align=center src=\"ical.png\" alt=\"calendar icon\"></a>",m_title);      
      //fprintf(pFile,"<img src=\"ical.png\" alt=\"calendar icon\">");
      
      /* Get all the specific workout information */
      for (int i = 0; i < m_nW; i++) {
				//if (m_workouts[i].WorkoutGetExercises())
				m_workouts[i].WorkoutShowDetails(t, pFile);
      }
      
      /* Document body close */
      fprintf(pFile,"</body>\n");
      
      /* Document html close */
      fprintf(pFile,"</html>\n");
      
      break;
      
    case OutputDebug:
      
      // write to the stderr output for debugging
      pFile = stdout;
      
      fprintf(pFile, "-----------Debugging workout program to STDIO-----------\n");
      
      /* Document title */
      GetTitle(t,pFile);
      
      /* Display all the exercise goals */
      ExerciseGoalsShowAll(t, pFile);
      
      /* Get all the specific workout information */
      for (int i = 0; i < m_nW; i++) {
				m_workouts[i].WorkoutShowDetails(t, pFile);
      }
      
      break;
      
      
    default:
      break;
  }
  
  
  /* Exit and close up our documents */
  if (pFile)
    fclose(pFile); // Close the output file   
  
}


////////////////////////////////////////////////////////////////////////////////

bool
WorkoutProgram::AlternateSquat(Workout *pW, 
															 bool *pBS,
															 setType s,
															 bool bOptional /* = false */,
															 weightCalcProc calcProc /* = calcSine */,
															 float fBaseCalcPercent /* = 0.6f */)
{
  
  
  // if the bool is BS, then we do front squats
  // alternate
  if (*pBS) 
    pW->AddExercise(BackSquat, 
										s, 
										ExerciseGoalGet(BackSquat),
										0, 
										calcProc,
										filterNothing,
										bOptional,
										fBaseCalcPercent);
  
  else
    pW->AddExercise(FrontSquat, 
										s, 
										ExerciseGoalGet(FrontSquat),
										0, 
										calcProc,
										filterNothing,
										bOptional,
										fBaseCalcPercent);
  
  // Switch the flag
  *pBS = (*pBS == true) ? false : true;
  
  return true;
}

////////////////////////////////////////////////////////////////////////////////


bool
WorkoutProgram::AddCombination(Workout *pW, 
															 int *pIndex,
															 setType s,
															 bool bOptional /* = false */,
															 weightCalcProc calcProc /* = calcSine */)
{
  
  // Select the next combination
  // the number should be equal to the number of cases below
  if (*pIndex > 8) *pIndex = 0;
  
  switch (*pIndex) {
    case 0:
      pW->AddExercise(CombinationI, 
											s, 
											ExerciseGoalGet(CombinationI),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;
    
		case 1:
      pW->AddExercise(CombinationA, 
											s, 
											ExerciseGoalGet(CombinationA),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;
      
    case 2:
      pW->AddExercise(CombinationB, 
											s, 
											ExerciseGoalGet(CombinationB),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;	
      
    case 3:
      pW->AddExercise(CombinationC, 
											s, 
											ExerciseGoalGet(CombinationC),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;
      
    case 4:
      pW->AddExercise(CombinationD, 
											s, 
											ExerciseGoalGet(CombinationD),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;
      
    case 5:
      pW->AddExercise(CombinationE, 
											s, 
											ExerciseGoalGet(CombinationE),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;		
      
    case 6:
      pW->AddExercise(CombinationF, 
											s, 
											ExerciseGoalGet(CombinationF),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;					
      
    case 7:
      pW->AddExercise(CombinationG, 
											s, 
											ExerciseGoalGet(CombinationG),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;					
      
  }
  
  *pIndex = *pIndex + 1;
  
  return true;
}

////////////////////////////////////////////////////////////////////////////////


bool
WorkoutProgram::RandomJerk(Workout *pW, 
													 setType s,
													 bool bOptional /* = false */,
													 weightCalcProc calcProc /* = calcSine */)
{
  
  ExerciseType e;
  
  // Select the next combination
  // the number should be equal to the number of cases below
  // Select a randomly seeded core exercise
  int temp = random() % 6;
  
  switch (temp) {		
    case 1:
    case 2:
      e = JerkDips;    
      break;
    case 3:		
    case 4:
      e = JerkPause;     
      break; 		
      
    default:
      e = JerkRack;    
      break;					
      
  }
  
  // Create and add the exercise
  pW->AddExercise(e, 
									s, 
									ExerciseGoalGet(e),
									0, 
									calcProc,
									filterOlympic,
									bOptional);  
  
  
  return true;
}

////////////////////////////////////////////////////////////////////////////////


bool
WorkoutProgram::RandomSnatch(Workout *pW, 
														 setType s,
														 bool bOptional /* = false */,
														 weightCalcProc calcProc /* = calcSine */)
{
  
  ExerciseType e;
  
  // Select the next combination
  // the number should be equal to the number of cases below
  // Select a randomly seeded core exercise
  int temp = rand() % 9;
  
  switch (temp) {		
    case 1:
      e = SnatchThreeParts;     
      break;
      
    case 2:
      e = SnatchMuscle;     
      break;
      
    case 3:
      e = PowerSnatchPlusSnatch;
      break;
      
    case 4:
      e = PowerSnatchPlusTwoSnatch;
      break;	
      
    case 5: 
      e = SnatchPower;
      break;
      
    case 6: 
      e = SnatchDrop;
      break;			
      
    default:
      e = Snatch;
      break;					
      
  }
  
  // Create and add the exercise
  pW->AddExercise(e, 
									s, 
									ExerciseGoalGet(e),
									0, 
									calcProc,
									filterOlympic,
									bOptional);   
  
  
  return true;
}

////////////////////////////////////////////////////////////////////////////////


bool
WorkoutProgram::RandomPull(Workout *pW, 
														 setType s,
														 bool bOptional /* = false */,
														 weightCalcProc calcProc /* = calcSine */, 
													   float adjPercent /* = 1.0f */)
{
  
  ExerciseType e;
  
  // Select the next combination
  // the number should be equal to the number of cases below
  // Select a randomly seeded core exercise
	//unsigned int seed = (unsigned int)time(NULL);
	//srand(seed);
	
  int temp = rand() % 5;
	
	printf("Random Pull temp seed: %d\n", temp);
  
  switch (temp) {
			
    case 1:
      e = SnatchPull;     
      break;
      
    case 2:
      e = CleanHighPull;     
      break;
      
    case 3:
      e = CleanPull;
      break;
      
    case 4:
      e = SnatchShrugs;
      break;	
      
    default:
      e = SnatchPull;
      break;					
      
  }
  
  // Create and add the exercise
  pW->AddExercise(e, 
									s, 
									ExerciseGoalGetAdjustment(e, adjPercent, 1.0f),
									0, 
									calcProc,
									filterNothing,
									bOptional);   
  
  
  return true;
}


////////////////////////////////////////////////////////////////////////////////


bool
WorkoutProgram::RandomClean(Workout *pW, 
														setType s,
														bool bOptional /* = false */,
														weightCalcProc calcProc /* = calcSine */)
{
  
  ExerciseType e;
  
  // Select the next combination
  // the number should be equal to the number of cases below
  // Select a randomly seeded core exercise
  int temp = rand() % 5;
  
  switch (temp) {		
    case 1:
      e = CleanThreeParts;     
      break;
      
    case 2:
      e = CleanTwoParts;     
      break;		
      
    default:
      e = Clean;
      break;					
      
  }
  
  // Create and add the exercise
  pW->AddExercise(e, 
									s, 
									ExerciseGoalGet(e),
									0, 
									calcProc,
									filterNothing,
									bOptional);   
  
  
  return true;
}

////////////////////////////////////////////////////////////////////////////////


bool
WorkoutProgram::RandomPress(Workout *pW, 
														setType s,
														bool bOptional /* = false */,
														weightCalcProc calcProc /* = calcSine */,
														float adjPercent /* 1.0 */)
{
  
  // Select the next combination
  // the number should be equal to the number of cases below
  // Select a randomly seeded core exercise
  int temp = random() % 5;
  
  switch (temp) {		
    case 1:
      pW->AddExercise(PPBN, 
											s, 
											ExerciseGoalGetAdjustment(PPBN, adjPercent, 1.0f),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;
    case 2:		
      pW->AddExercise(PPress, 
											s, 
											ExerciseGoalGetAdjustment(PPress, adjPercent, 1.0f),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;   
    case 3:		
      pW->AddExercise(PBN, 
											s, 
											ExerciseGoalGetAdjustment(PBN, adjPercent, 1.0f),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;			
      
    default:
      pW->AddExercise(Press, 
											s, 
											ExerciseGoalGetAdjustment(Press, adjPercent, 1.0f),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;					
      
  }
  
  
  return true;
}


////////////////////////////////////////////////////////////////////////////////


bool
WorkoutProgram::RandomCore(Workout *pW, 
													 setType s,
													 bool bOptional /* = false */,
													 weightCalcProc calcProc /* = calcSine */)
{
  
  // Select the next combination
  // the number should be equal to the number of cases below
  // Select a randomly seeded core exercise
  int temp = random() % 9;
  
  switch (temp) {			
      
    default:
      pW->AddExercise(Crunches, 
											s, 
											ExerciseGoalGet(Crunches),
											0, 
											calcProc,
											filterNothing,
											bOptional);      
      break;					
      
  }
  
  
  return true;
}


////////////////////////////////////////////////////////////////////////////////

int
main (int argc, char* argv[]) 
{
  
  char myFile[32];
  int weeks = 12;
  int startdate = 20100503;
  int cnj = -1;
  int run = -1;
  int backsquat = -1;
  int frontsquat = -1;
  int press  = -1;
  int snatch = -1;
  int deadlift = -1;
	int pullup = -1;
  int workouttype = 0;
  char daysFlag = -1;
	int basePercent = 60;
  
  WorkoutProgram *pW;
  
  if (argc < 2) {
    // Inform the user of how to use the program
    printf("Usage is -f <outputfile> -d <startdate> -w <weeks> -<ExcerciseType> <value> ...\n"); 
    printf("Details:\n");
    printf("\t-d <startdate> where input is ISO8601 formatted start date.\n"
					 "    This value should be the Sunday of the desired start week\n");
    printf("Non optional input arguments:\n");
    printf("\t-CleanAndJerk clean and jerk goal weight (lbs)\n");
    printf("\t\tor, alternatively\n");
    printf("\t-BodyWeight bodyweight (lbs)\n");
    
    printf("Optional input arguments:\n");
    printf("\t-Snatch snatch goal weight\n");
    printf("\t-Running running distance goal\n");
    printf("\t-Press press goal\n");
    
    printf("\nExample: workoutProgram -f myfile -t RunningFIRST\n");
    exit(0);
  } else { // if we got enough parameters...
    
    // Parse through the command line arguements
    for (int i = 1; i < argc; i++) {
#if 0
      printf("argv[%d]: %s\n",i, argv[i]);
#endif		
      
      // Get the file name for output
      if (!strcmp(argv[i], "-f")) {
				strcpy(myFile, argv[i + 1]);
      }
      
      // Get the the start date if specified
      if (!strcmp(argv[i], "-d")) {
				startdate = atoi(argv[i + 1]);
      }
      
      // Get the the number of weeks for the program
      if (!strcmp(argv[i], "-w")) {
				weeks = atoi(argv[i + 1]);
      }
      
      // Get the the week day flag
      if (!strcmp(argv[i], "-df")) {
				daysFlag = (char)atoi(argv[i + 1]);
      }			
			
			if (!strcmp(argv[i], "-b")) {
				basePercent = (char)atoi(argv[i + 1]);
      }		
      
      if (!strcmp(argv[i], "-CleanAndJerk") || !strcmp(argv[i], "-BodyWeight")) {
				cnj = atoi(argv[i + 1]);
      }
      
      if (!strcmp(argv[i], "-Snatch")) {
				snatch = atoi(argv[i + 1]);
      }
      
      if (!strcmp(argv[i], "-BackSquat")) {
				backsquat = atoi(argv[i + 1]);
      }
      
      if (!strcmp(argv[i], "-FrontSquat")) {
				frontsquat = atoi(argv[i + 1]);
      }
      
      if (!strcmp(argv[i], "-Deadlift")) {
				deadlift = atoi(argv[i + 1]);
      }
      
      if (!strcmp(argv[i], "-Press")) {
				press = atoi(argv[i + 1]);
      }
			
			if (!strcmp(argv[i], "-Pullup")) {
				pullup = atoi(argv[i + 1]);
      }
      
      if (!strcmp(argv[i], "-Running")) {
				run = atoi(argv[i + 1]);
      }
      
      if (!strcmp(argv[i], "-t")) {
				
				// match on the type based on input parameters
				if (!strcmp(argv[i + 1], "RunningFIRST")) {
					workouttype = 1;
				}
				else if (!strcmp(argv[i + 1], "OlympicLifting")) {
					workouttype = 0;
				}
				else if (!strcmp(argv[i + 1], "OlympicMaintenance")) {
					workouttype = 2;
				}
				else if (!strcmp(argv[i + 1], "ClimbingTraining")) {
					workouttype = 3;
				}				
				else if (!strcmp(argv[i + 1], "OlympicExtended")) {
					workouttype = 4;
				}
				else if (!strcmp(argv[i + 1], "Core")) {
					workouttype = 5;
				}
				else if (!strcmp(argv[i + 1], "Contest900")) {
					workouttype = 6;
				}
      }
      
    }
  }
  
  //////////////////////////////////////////////////////////////////////////////
  // Define the type of workout based on input parameters
  
  switch (workouttype) {
		case 0: // olympic cycle
      pW = new WorkoutProgram(myFile, startdate, OlympicTrainingProc, weeks);
      break;
    case 5: // core builder
      pW = new WorkoutProgram(myFile, startdate, CoreBuilderProc, weeks);
      break;
		case 6: // olympic contest 900
			weeks = 4;
      pW = new WorkoutProgram(myFile, startdate, OlympicContest900Proc, weeks);
      break;	
//    case 1: // running style
//      pW = new RunningFIRST(myFile, startdate, weeks);
//      break;
//    case 2: // olympic maintenance
//      pW = new OlympicMaintenance(myFile, startdate, weeks);
//      break;			
//    case 3: // climbing training
//      pW = new ClimbingTraining(myFile, startdate, weeks);
//      break;
//    case 4: // olympic extended
//      pW = new OlympicExtended(myFile, startdate, weeks);
//      break;				
    default:
      pW = new WorkoutProgram(myFile, startdate, OlympicMaintenanceProc, weeks);
      break;
  }
  
  // set the week day flag if passed in
  if (daysFlag > 0)
    pW->SetWorkoutDaysFlag(daysFlag);
  
  pW->SetWorkoutDaysFlag(TUESDAY | THURSDAY | SATURDAY);
	
	pW->SetBasePercent(basePercent);
  
  //////////////////////////////////////////////////////////////////////////////
  // Set the goals as desired
  if (cnj > 0)
    pW->ExerciseGoalSet(CleanAndJerk, cnj);
  
  if (snatch > 0)
    pW->ExerciseGoalSet(Snatch, snatch);
  
  if (backsquat > 0)
    pW->ExerciseGoalSet(BackSquat, backsquat);
  
  if (frontsquat > 0)
    pW->ExerciseGoalSet(FrontSquat, frontsquat);
  
  if (press > 0)
    pW->ExerciseGoalSet(Press, press);
  
  if (deadlift > 0)
    pW->ExerciseGoalSet(Deadlift, deadlift);
  
  if (run > 0)
    pW->ExerciseGoalSet(Running, run);
	
	if (pullup > 0)
    pW->ExerciseGoalSet(Pullups, pullup);
  
  //////////////////////////////////////////////////////////////////////////////
  // Crank out the session
  pW->GenerateSession();
  
  // clean up
  delete pW;
  
  
  return 0;
  
}
